#include "core_tcp.h"

static void _core_aiot_tcp_exec_inc(core_tcp_handle_t *tcp_handle)
{
    tcp_handle->sysdep->core_sysdep_mutex_lock(tcp_handle->data_mutex);
    tcp_handle->exec_count++;
    tcp_handle->sysdep->core_sysdep_mutex_unlock(tcp_handle->data_mutex);
}

static void _core_aiot_tcp_exec_dec(core_tcp_handle_t *tcp_handle)
{
    tcp_handle->sysdep->core_sysdep_mutex_lock(tcp_handle->data_mutex);
    tcp_handle->exec_count--;
    tcp_handle->sysdep->core_sysdep_mutex_unlock(tcp_handle->data_mutex);
}

static void _core_tcp_auth_recv_handler(void *handle, const aiot_tcp_recv_t *packet, void *userdata)
{
    core_tcp_handle_t *tcp_handle = (core_tcp_handle_t *)handle;
    core_tcp_response_t *response = (core_tcp_response_t *)userdata;

    switch (packet->type) {
        case AIOT_TCPRECV_AUTH: {
            core_log1(tcp_handle->sysdep, 0x01, "auth recv: %s\n", packet->data.body.buffer);
        }
        break;
        case AIOT_TCPRECV_BODY: {
            core_log1(tcp_handle->sysdep, 0x01, "body recv: %s\n", packet->data.body.buffer);
            uint8_t *content = tcp_handle->sysdep->core_sysdep_malloc(response->content_len + packet->data.body.len + 1,
                               CORE_TCP_MODULE_NAME);
            if (content == NULL) {
                return;
            }
            memset(content, 0, response->content_len + packet->data.body.len + 1);
            if (response->content != NULL) {
                memcpy(content, response->content, response->content_len);
                tcp_handle->sysdep->core_sysdep_free(response->content);
            }
            memcpy(content + response->content_len, packet->data.body.buffer, packet->data.body.len);
            response->content = content;
            response->content_len = response->content_len + packet->data.body.len;
        }
        break;
        default: {

        }
        break;
    }
}

static void _core_tcp_recv_handler(void *handle, const aiot_tcp_recv_t *packet, void *userdata)
{
    core_tcp_handle_t *tcp_handle = (core_tcp_handle_t *)handle;
    core_tcp_response_t *response = (core_tcp_response_t *)userdata;

    if (tcp_handle->recv_handler == NULL) {
        return;
    }

    switch (packet->type) {
        case AIOT_TCPRECV_AUTH: {
            if ((strlen(packet->data.header.key) == strlen("Content-Length")) &&
                (memcmp(packet->data.header.key, "Content-Length", strlen(packet->data.header.key)) == 0)) {
                core_str2uint(packet->data.header.value, (uint8_t)strlen(packet->data.header.value), &response->content_total_len);
            }
            tcp_handle->recv_handler(tcp_handle, packet, tcp_handle->userdata);
        }
        break;
        case AIOT_TCPRECV_BODY: {
            uint8_t *content = tcp_handle->sysdep->core_sysdep_malloc(response->content_len + packet->data.body.len + 1,
                               CORE_TCP_MODULE_NAME);
            if (content == NULL) {
                return;
            }
            memset(content, 0, response->content_len + packet->data.body.len + 1);
            if (response->content != NULL) {
                memcpy(content, response->content, response->content_len);
                tcp_handle->sysdep->core_sysdep_free(response->content);
            }
            memcpy(content + response->content_len, packet->data.body.buffer, packet->data.body.len);
            response->content = content;
            response->content_len = response->content_len + packet->data.body.len;
        }
        break;
        default: {

        }
        break;
    }
}

static int32_t _core_tcp_send_auth(core_tcp_handle_t *tcp_handle, uint8_t *auth_config, uint32_t len)
{
    int32_t res = STATE_SUCCESS;

    res = core_tcp_send(tcp_handle, auth_config, len);

    return res;
}

static int32_t _core_tcp_recv_auth(core_tcp_handle_t *tcp_handle, core_tcp_response_t *response)
{
    int32_t res = STATE_SUCCESS;
    char *token = NULL;
    uint32_t token_len = 0;
    uint64_t timenow_ms = tcp_handle->sysdep->core_sysdep_time();

    while (1) {
        if (timenow_ms >= tcp_handle->sysdep->core_sysdep_time()) {
            timenow_ms = tcp_handle->sysdep->core_sysdep_time();
        }
        if (tcp_handle->sysdep->core_sysdep_time() - timenow_ms >= tcp_handle->auth_timeout_ms) {
            break;
        }

        res = core_tcp_recv_auth(tcp_handle);
        if (res < STATE_SUCCESS) {
            break;
        }
    }

    if (res < STATE_SUCCESS) {
        if (res != STATE_TCP_READ_BODY_FINISHED) {
            return res;
        } else {
            res = STATE_SUCCESS;
        }
    } else {
        return STATE_TCP_AUTH_NOT_FINISHED;
    }

    if (response->content == NULL || response->content_len != response->content_total_len) {
        return STATE_TCP_AUTH_NOT_EXPECTED;
    }

    core_log2(tcp_handle->sysdep, STATE_TCP_LOG_AUTH, "|||%.*s\r\n", &response->content_len, response->content);

    res = core_json_value((const char *)response->content, response->content_len, "get_config", strlen("get_config"), &token,
                          &token_len);
    if (res < STATE_SUCCESS) {
        return STATE_TCP_AUTH_TOKEN_FAILED;
    }

    tcp_handle->sysdep->core_sysdep_mutex_lock(tcp_handle->data_mutex);
    if (tcp_handle->token != NULL) {
        tcp_handle->sysdep->core_sysdep_free(tcp_handle->token);
        tcp_handle->token = NULL;
    }
    tcp_handle->token = tcp_handle->sysdep->core_sysdep_malloc(token_len + 1, CORE_TCP_MODULE_NAME);
    if (tcp_handle->token == NULL) {
        tcp_handle->sysdep->core_sysdep_mutex_unlock(tcp_handle->data_mutex);
        return STATE_SYS_DEPEND_MALLOC_FAILED;
    }
    memset(tcp_handle->token, 0, token_len + 1);
    memcpy(tcp_handle->token, token, token_len);
    tcp_handle->sysdep->core_sysdep_mutex_unlock(tcp_handle->data_mutex);

    return STATE_SUCCESS;
}

void *aiot_tcp_init(void)
{
    core_tcp_handle_t *tcp_handle = NULL;

    tcp_handle = core_tcp_init();
    if (tcp_handle == NULL) {
        return NULL;
    }

    tcp_handle->auth_timeout_ms = CORE_TCP_DEFAULT_AUTH_TIMEOUT_MS;
    tcp_handle->long_connection = 1;

    tcp_handle->exec_enabled = 1;

    return tcp_handle;
}

int32_t aiot_tcp_setopt(void *handle, aiot_tcp_option_t option, void *data)
{
    int32_t res = STATE_SUCCESS;
    core_tcp_handle_t *tcp_handle = (core_tcp_handle_t *)handle;

    if (tcp_handle == NULL || data == NULL) {
        return STATE_USER_INPUT_NULL_POINTER;
    }

    if (option >= AIOT_TCPOPT_MAX) {
        return STATE_USER_INPUT_OUT_RANGE;
    }

    if (tcp_handle->exec_enabled == 0) {
        return STATE_USER_INPUT_EXEC_DISABLED;
    }

    _core_aiot_tcp_exec_inc(tcp_handle);

    tcp_handle->sysdep->core_sysdep_mutex_lock(tcp_handle->data_mutex);
    switch (option) {
        case AIOT_TCPOPT_HOST:
        case AIOT_TCPOPT_PORT:
        case AIOT_TCPOPT_NETWORK_CRED:
        case AIOT_TCPOPT_CONNECT_TIMEOUT_MS:
        case AIOT_TCPOPT_SEND_TIMEOUT_MS:
        case AIOT_TCPOPT_RECV_TIMEOUT_MS:
        case AIOT_TCPOPT_DEINIT_TIMEOUT_MS:
        case AIOT_TCPOPT_HEADER_BUFFER_LEN:
        case AIOT_TCPOPT_BODY_BUFFER_LEN:
        case AIOT_TCPOPT_EVENT_HANDLER: {
            tcp_handle->sysdep->core_sysdep_mutex_unlock(tcp_handle->data_mutex);
            res = core_tcp_setopt(handle, (core_tcp_option_t)option, data);
            tcp_handle->sysdep->core_sysdep_mutex_lock(tcp_handle->data_mutex);
        }
        break;
        case AIOT_TCPOPT_USERDATA: {
            tcp_handle->userdata = data;
        }
        break;
        case AIOT_TCPOPT_RECV_HANDLER: {
            tcp_handle->recv_handler = (aiot_tcp_recv_handler_t)data;
        }
        break;
        case AIOT_TCPOPT_EXTEND_DEVINFO: {
            res = core_strdup(tcp_handle->sysdep, &tcp_handle->extend_devinfo, (char *)data, CORE_TCP_MODULE_NAME);
        }
        break;
        case AIOT_TCPOPT_AUTH_TIMEOUT_MS: {
            tcp_handle->auth_timeout_ms = *(uint32_t *)data;
        }
        break;
        case AIOT_TCPOPT_LONG_CONNECTION: {
            tcp_handle->long_connection = *(uint8_t *)data;
        }
        break;
        default: {
            res = STATE_USER_INPUT_UNKNOWN_OPTION;
        }
        break;
    }
    tcp_handle->sysdep->core_sysdep_mutex_unlock(tcp_handle->data_mutex);

    _core_aiot_tcp_exec_dec(tcp_handle);

    return res;
}

int32_t aiot_tcp_connect_auth(void *handle, uint8_t *auth_data, uint32_t auth_len)
{
    int32_t res = STATE_SUCCESS;
    core_tcp_response_t response;
    core_tcp_handle_t *tcp_handle = (core_tcp_handle_t *)handle;

    if (tcp_handle == NULL) {
        return STATE_USER_INPUT_NULL_POINTER;
    }

    if (tcp_handle->exec_enabled == 0) {
        return STATE_USER_INPUT_EXEC_DISABLED;
    }

    _core_aiot_tcp_exec_inc(tcp_handle);

    memset(&response, 0, sizeof(core_tcp_response_t));

    if ((res = core_tcp_setopt(tcp_handle, CORE_TCPOPT_RECV_HANDLER,
                                (void *)_core_tcp_auth_recv_handler)) < STATE_SUCCESS ||
        (res = core_tcp_setopt(tcp_handle, CORE_TCPOPT_USERDATA, (void *)&response)) < STATE_SUCCESS) {
        return res;
    }
    if (tcp_handle->network_handle == NULL ||
        (tcp_handle->network_handle != NULL && tcp_handle->long_connection == 0)) {
        if ((res = core_tcp_connect(tcp_handle)) < STATE_SUCCESS) {
            _core_aiot_tcp_exec_dec(tcp_handle);
            return res;
        }
    }

    /* send auth request (get_config) */
    res = _core_tcp_send_auth(tcp_handle, auth_data, auth_len);
    if (res < STATE_SUCCESS) {
        _core_aiot_tcp_exec_dec(tcp_handle);
        return res;
    }
    /* recv auth response （get_config） */
    res = _core_tcp_recv_auth(tcp_handle, &response);
    if (response.content != NULL) {
        tcp_handle->sysdep->core_sysdep_free(response.content);
    }

    _core_aiot_tcp_exec_dec(tcp_handle);

    return res;
}

int32_t aiot_tcp_send(void *handle, uint8_t *payload, uint32_t payload_len)
{
    int32_t res = STATE_SUCCESS;
    core_tcp_handle_t *tcp_handle = (core_tcp_handle_t *)handle;

    if (tcp_handle == NULL || payload == NULL) {
        return STATE_USER_INPUT_NULL_POINTER;
    }
    if (payload_len == 0) {
        return STATE_USER_INPUT_OUT_RANGE;
    }
    if (tcp_handle->exec_enabled == 0) {
        return STATE_USER_INPUT_EXEC_DISABLED;
    }

    _core_aiot_tcp_exec_inc(tcp_handle);

    if (tcp_handle->network_handle == NULL ||
        (tcp_handle->network_handle != NULL && tcp_handle->long_connection == 0)) {
        if ((res = core_tcp_connect(tcp_handle)) < STATE_SUCCESS) {
            _core_aiot_tcp_exec_dec(tcp_handle);
            return res;
        }
    }

    res = core_tcp_send(tcp_handle, payload, payload_len);

    _core_aiot_tcp_exec_dec(tcp_handle);

    return res;
}

int32_t aiot_tcp_recv(void *handle)
{
    int32_t res = STATE_SUCCESS;
    uint64_t timenow_ms = 0;
    core_tcp_response_t response;
    core_tcp_handle_t *tcp_handle = (core_tcp_handle_t *)handle;

    if (tcp_handle == NULL) {
        return STATE_USER_INPUT_NULL_POINTER;
    }
    if (tcp_handle->network_handle == NULL) {
        return STATE_SYS_DEPEND_NWK_CLOSED;
    }
    if (tcp_handle->exec_enabled == 0) {
        return STATE_USER_INPUT_EXEC_DISABLED;
    }

    _core_aiot_tcp_exec_inc(tcp_handle);

    memset(&response, 0, sizeof(core_tcp_response_t));
    if ((res = core_tcp_setopt(tcp_handle, CORE_TCPOPT_RECV_HANDLER, (void *)_core_tcp_recv_handler)) < STATE_SUCCESS ||
        (res = core_tcp_setopt(tcp_handle, CORE_TCPOPT_USERDATA, (void *)&response)) < STATE_SUCCESS) {
        return res;
    }
    timenow_ms = tcp_handle->sysdep->core_sysdep_time();
    while (1) {
        if (timenow_ms >= tcp_handle->sysdep->core_sysdep_time()) {
            timenow_ms = tcp_handle->sysdep->core_sysdep_time();
        }
        if (tcp_handle->sysdep->core_sysdep_time() - timenow_ms >= tcp_handle->recv_timeout_ms) {
            break;
        }

        res = core_tcp_recv(tcp_handle);
        if (res < STATE_SUCCESS) {
            break;
        }
    }

    if (res < STATE_SUCCESS) {
        if (res != STATE_TCP_READ_BODY_FINISHED) {
            _core_aiot_tcp_exec_dec(tcp_handle);
            if (response.content != NULL) {
                tcp_handle->sysdep->core_sysdep_free(response.content);
            }
            return res;
        } else {
            res = STATE_SUCCESS;
        }
    } else {
        return STATE_TCP_RECV_NOT_FINISHED;
    }

    if (tcp_handle->recv_handler != NULL) {
        aiot_tcp_recv_t packet;

        packet.type = AIOT_TCPRECV_BODY;
        packet.data.body.buffer = response.content;
        packet.data.body.len = response.content_len;

        tcp_handle->recv_handler(tcp_handle, &packet, tcp_handle->userdata);
    }
    if (response.content != NULL) {
        tcp_handle->sysdep->core_sysdep_free(response.content);
    }

    _core_aiot_tcp_exec_dec(tcp_handle);

    return res;
}

int32_t aiot_tcp_deinit(void **p_handle)
{
    uint64_t deinit_timestart = 0;
    core_tcp_handle_t *tcp_handle = NULL;

    if (p_handle == NULL || *p_handle == NULL) {
        return STATE_USER_INPUT_NULL_POINTER;
    }

    tcp_handle = *(core_tcp_handle_t **)p_handle;

    if (tcp_handle->exec_enabled == 0) {
        return STATE_USER_INPUT_EXEC_DISABLED;
    }

    tcp_handle->exec_enabled = 0;
    deinit_timestart = tcp_handle->sysdep->core_sysdep_time();
    do {
        if (tcp_handle->exec_count == 0) {
            break;
        }
        tcp_handle->sysdep->core_sysdep_sleep(CORE_TCP_DEINIT_INTERVAL_MS);
    } while ((tcp_handle->sysdep->core_sysdep_time() - deinit_timestart) < tcp_handle->deinit_timeout_ms);

    if (tcp_handle->exec_count != 0) {
        return STATE_TCP_DEINIT_TIMEOUT;
    }

    if (tcp_handle->product_key != NULL) {
        tcp_handle->sysdep->core_sysdep_free(tcp_handle->product_key);
    }
    if (tcp_handle->device_name != NULL) {
        tcp_handle->sysdep->core_sysdep_free(tcp_handle->device_name);
    }
    if (tcp_handle->device_secret != NULL) {
        tcp_handle->sysdep->core_sysdep_free(tcp_handle->device_secret);
    }
    if (tcp_handle->extend_devinfo != NULL) {
        tcp_handle->sysdep->core_sysdep_free(tcp_handle->extend_devinfo);
    }
    if (tcp_handle->token != NULL) {
        tcp_handle->sysdep->core_sysdep_free(tcp_handle->token);
    }

    core_tcp_deinit(p_handle);

    return STATE_SUCCESS;
}

